iT邦幫忙

2024 iThome 鐵人賽

DAY 9
0
Software Development

Django 2024: 從入門到SaaS實戰系列 第 10

Django in 2024: 那些你可能會想在後台使用的第三方庫

  • 分享至 

  • xImage
  •  

在先前的例子中,我們學習如何針對我們對於數據上的需求去修改預設的後台

但是都選擇使用Django這樣具有豐富第三方庫的框架,了解人家造好什麼輪子也是很重要的事情

程式碼:https://github.com/class83108/django_project/tree/3rd_package/

今日的重點如下:

  • 妝點Django Admin:django-jazzmin
  • 更好的處理網站內容:django-mdeditor
  • 監控與優化資料庫查詢:django-debug-toolbar
  • 想要輕鬆生成圖表?django整合charts

這些套件的文檔都會放在文章最下方的參考資料中

妝點Django Admin:django-jazzmin

https://github.com/iamfoysal/Best-Django-Admin-interface

上面的連結中有許多套件可供選擇,都可以修改後台預設的外觀
而網站好不好看是相當主觀的事情,我這邊介紹django-jazzmin有幾個因素:

  1. 我喜歡他的樣式,並且是使用bootstrap4為基礎開發,自己再針對程式碼微調很方便
  2. 直到現在都還有持續在維護,Django 2.0以上都適用

那就直接進入主題吧

  • 安裝套件
poetry add django-jazzmin
  • 為了避免等等選取語言會有錯誤需要做配置
# settings.py
MIDDLEWARE = [
		...
    "django.middleware.locale.LocaleMiddleware",
    ...
]

LANGUAGES = [
    ("en", "English"),
    ("zh-hant", "繁體中文"),
    # 添加其他你想支持的語言
]

USE_I18N = True

# 根目錄的urls.py
from django.conf.urls.i18n import i18n_patterns

urlpatterns = [
    path("i18n/", include("django.conf.urls.i18n")),
]

urlpatterns += i18n_patterns(
    path("admin/", admin_site.urls),
	  ....
	  # 以及其他路由
)
  • 進行設定,在JAZZMIN_SETTINGS中可調整的選項很多,完整可以去看文檔,這邊只簡介其中幾樣
# settings.py
INSTALLED_APPS = [
    # 控制後台主题
    "jazzmin", # 一定要放在admin前面
    "django.contrib.admin",
    ...
]

https://ithelp.ithome.com.tw/upload/images/20240921/20161866RszIpBwe1q.png

我們看一下還能做什麼調整

JAZZMIN_SETTINGS = {
    # 控制網站title
    "site_title": "後台管理",
    # 控制sidebar的上方標題
    "site_brand": "Library",
    # 可以在這裡設定logo
    # "site_logo": "books/img/logo.png",
    # 控制登入頁面的logo 可以之後用request.user來判斷要顯示哪一個logo
    "login_logo": None,
    # 控制footer的版權資訊
    "copyright": "Acme Library Ltd",
    # 控制上方的搜尋欄可以搜尋的model,並且搭配admin的search_fields
    "search_model": ["auth.User", "auth.Group", "article.ArticleV2"],
    ############
    # Top Menu #
    ############
    # 上方的navbar
    "topmenu_links": [
        # 除了控制顯示的名稱,也可以控制連結的url,並且可以設定權限
        {"name": "Home", "url": "admin:index", "permissions": ["auth.view_user"]},
        # 可以設定新開視窗
        {
            "name": "Support",
            "url": "https://github.com/farridav/django-jazzmin/issues",
            "new_window": True,
        },
        # 可以設定要去哪個model的頁面 會檢查是否有model的權限
        {"model": "auth.User"},
        # 可以設定要去哪個app的頁面 會檢查是否有app的權限
        {"app": "article"},
    ],
    #############
    # User Menu #
    #############
    # 控制上方user的menu 還能有什麼額外的連結
    "usermenu_links": [
        {
            "name": "Support",
            "url": "https://github.com/farridav/django-jazzmin/issues",
            "new_window": True,
        },
        {"model": "auth.user"},
    ],
    #############
    # Side Menu #
    #############
    # 可以添加自定義的連結
    "custom_links": {
        "article": [
            {
                "name": "Make Messages",
                "url": "make_messages",
                "icon": "fas fa-comments",
                "permissions": ["auth.view_user"],
            }
        ]
    },
    #################
    # Related Modal #
    #################
    # 控制是否顯示related modal還是poup window
    "related_modal_active": False,
    #############
    # UI Tweaks #
    #############
    # 可以添加自定義的css和js
    "custom_css": None,
    "custom_js": None,
    # 控制是否顯示UI建構器
    "show_ui_builder": True,
    # 可以控制是否顯示語言選擇器
    "language_chooser": True,
}

https://ithelp.ithome.com.tw/upload/images/20240921/20161866ZRerRbC5Og.png

相比之前我們需要去寫許多程式碼來做一些客製化的需求,使用這樣的套件是不是方便許多

更好的處理網站內容:django-mdeditor

有關markdown編輯器,也有不同的選擇,例如

  • django-markdownx:https://github.com/neutronX/django-markdownx
  • 或是JS輕量的庫:https://github.com/sparksuite/simplemde-markdown-editor

依照個人習慣選擇,對我個人而言,需要能很好的控制上傳圖片功能,並且能夠辨認table等的markdown格式(有的還真的無法辨認),使用django-mdeditor對於上述兩點需求都能做到

  • 安裝
poetry add django-mdeditor
  • 配置
# settings.py
INSTALLED_APPS = [
        ...
        'mdeditor',
]

# 如果是Django版本大於等於3
X_FRAME_OPTIONS = 'SAMEORIGIN' 

# 這個我們之前就配置過了 不過要在根目錄下建立uploads資料夾
MEDIA_ROOT = os.path.join(BASE_DIR, 'uploads')
MEDIA_URL = '/media/'

# urls.py
urlpatterns = [
    path("mdeditor/", include("mdeditor.urls")),
]
# 需要注意要放在他前面,不然上傳圖片會有問題
urlpatterns += [
    path("i18n/", include("django.conf.urls.i18n")),
]

# article.models.py
from mdeditor.fields import MDTextField

class ArticleV2(models.Model):
    ...
    content = MDTextField(verbose_name="內容")
    
# bash
python3 manage.py makemigrations
python3 manage.py migrate

https://ithelp.ithome.com.tw/upload/images/20240921/201618660kOkYUYhHt.png

另外點擊圖片的彈窗會跑版,所以可以針對這個元素去調整css,至於該怎麼做,前面介紹很多了這邊就不贅述
https://ithelp.ithome.com.tw/upload/images/20240921/20161866nyNzV2vr6J.png

還有一點需要注意,在Django in 2024: 解鎖Django form的潛力中配置了form表單的markdown顯示等特殊功能。

如果要再自行調整會比較需要注意,因此當我自己有特殊邏輯需求時,我自己覺得搭配簡單的JS庫可能更容易,可以專注使用django form以及admin處理我的邏輯,但是如果是很單純需要快速開發,我就會選擇該套件

監控與優化資料庫查詢:django-debug-toolbar

Django使用ORM語法雖然讓許多查詢變得更直觀,但是也更應該注意是否寫了效能不好的寫法,甚至呼叫太多次資料庫造成多餘的負荷

  • 安裝套件
poetry add django-debug-toolbar==4.2.0
  • 配置
# settings.py

INSTALLED_APPS = [
    ...
    "debug_toolbar",
    ...
]

MIDDLEWARE = [

    # 放在第一個
    "debug_toolbar.middleware.DebugToolbarMiddleware",
    ...
]

INTERNAL_IPS = [
    "127.0.0.1",
]
#Debug Toolbar默認只在來自INTERNAL_IPS中列出的IP位置的請求中顯示

# urls.py
if settings.DEBUG:
    import debug_toolbar

    urlpatterns += [
        path("__debug__/", include(debug_toolbar.urls)),
    ]

我們就能在頁面上看到啦
https://ithelp.ithome.com.tw/upload/images/20240921/20161866ZnJRZ5vo3B.png

我們進到文章修改頁後點擊SQL的部分,可以看到在計算文章數量上重複了兩次
https://ithelp.ithome.com.tw/upload/images/20240921/20161866PlldyzkZ6o.png
https://ithelp.ithome.com.tw/upload/images/20240921/20161866geq7ywvzxe.png
https://ithelp.ithome.com.tw/upload/images/20240921/20161866PGpqWNbYAc.png

可以看到造成兩次查詢的原因分別為以下方法

  • full_result_count = self.root_queryset.count() :用來顯示總筆數
  • result_count = paginator.count :這是為了獲取當前頁的記錄數(result_count)。Django的分頁器使用這個數字來計算當前頁面應該顯示的記錄數量

我們可以透過這些資訊,來了解到是不是有需要設定相關的快取或是資料庫的索引進行優化,端看是寫入頻率遠小於讀取頻率的狀況

想要輕鬆生成圖表?django整合charts

我嘗試了像django-admin-charts等根據Model來生成資料的套件,發現可能會有幾個問題

  1. 跟其他的套件是整合度不高,需要處理許多模板標籤的問題
  2. 在後台生成圖表的表單我自己覺得沒有那麼直觀,且自由度不高

後來還是覺得回歸最單純的方法,讓Django做它擅長的事情:透過ORM提取資料,至於頁面的部分?交給JS來處理

我之前也有寫過一篇使用ECharts跟Django整合的文章

但是那邊是單純的只是用View返回JSON,這裡則是在後台進行整合

預先處理

因為我們的資料目前筆數真的不多,所以請AI直接根據我們的資料來生成對應的資料

我這裡ArticleV2的content欄位已經改為原本的JSONField並且遷移過了需要注意

# 修改欄位
content = JSONField(default=dict, verbose_name="內容")
# content = MDTextField(verbose_name="內容")

腳本的連結如下,直接執行就可以了

https://github.com/class83108/django_project/blob/3rd_package/django_project/add_articles.py

整體流程

資料都準備好了,那先說明我們的需求

  1. 我想要知道每一個tag在文章中被引用的次數
  2. 我們在自定義頁面中處理相關的圖表
  • 添加將相關js放到頁面中
# custom_admin_page.html
{% extends "admin/base_site.html" %}

{% block extrahead %}
{{ block.super }}

{% endblock extrahead %}

{% block content %}
<div>
	<canvas id="myChart"></canvas>
  </div>

  <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>

  <script>
	const ctx = document.getElementById('myChart');
  
	new Chart(ctx, {
	  type: 'bar',
	  data: {
		labels: ['Red', 'Blue', 'Yellow', 'Green', 'Purple', 'Orange'],
		datasets: [{
		  label: '# of Votes',
		  data: [12, 19, 3, 5, 2, 3],
		  borderWidth: 1
		}]
	  },
	  options: {
		scales: {
		  y: {
			beginAtZero: true
		  }
		}
	  }
	});
</script>
{% endblock %}

https://ithelp.ithome.com.tw/upload/images/20240921/201618668tRN8n5SoY.png

從上方的資料來看,我們只需要拿到label(x軸標籤)與其對應的資料datasets 就能完成我們的需求

  • 製作資料

因為可能不只一個頁面需要圖表,所以在跟目錄下建立utility資料夾,並設置:

  1. init.py:在python3.3之後如果要加目錄視作包不一定需要,但是設置後方便控制導入行為
  2. create_charts.py:我們製作圖表所需資料的邏輯
# create_charts.py
from article.models import Tag
from django.db.models import Count

import json

def get_tags_count() -> dict:
    tags_count = Tag.objects.annotate(count=Count("articlev2")).values_list(
        "name", "count"
    )
		
		# 做成JSON,如果用AJAX調用時也更方便
    return json.dumps(
        {
            "labels": [tag[0] for tag in tags_count],
            "data": [tag[1] for tag in tags_count],
        }
    )

# __init__.py
from .create_charts import get_tags_count
  • 視圖修改
# article.views.py
from utility import get_tags_count

import json

class CustomAdminPageView(TemplateView):

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)  # 獲取原始的上下文數據

        # 獲取傳遞的 AdminSite 實例
        admin_site = self.admin_site or self.request.site

        if admin_site:
            ...
            # 添加額外的上下文數據
            context.update(
                {
                    ...
                    # 新增這裡
                    "tag_count_result": json.loads(get_tags_count()),
                }
            )

        return context
  • 最後修改模板
# custom_admin_page.html
{% extends "admin/base_site.html" %}

{% block extrahead %}
{{ block.super }}

{% endblock extrahead %}

{% block content %}
<div class="container">
	<div class="row">
		<div class="col">
				<canvas id="myChart"></canvas>
		</div>
	</div>
</div>

  <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>

  <script>
	const ctx = document.getElementById('myChart');
  
	new Chart(ctx, {
	  type: 'bar',
	  data: {
		labels: {{ tag_count_result.labels | safe }}, # 如果是陣列就需要添加safe
		datasets: [{
		  label: 'Tag Count',
		  data: {{ tag_count_result.data | safe }},
		  borderWidth: 1
		}]
	  },
	  options: {
		scales: {
		  y: {
			beginAtZero: true
		  }
		}
	  }
	});
</script>

{% endblock %}

就能看到成果了!https://ithelp.ithome.com.tw/upload/images/20240921/20161866t7AJcukUdn.png

今日總結

今天我們透過使用不同的套件來修改後台

  1. 使用django-jazzmin來調整後台樣式,並且提供簡單的方式來管理Navbar與Sidebar資料
  2. 使用django-mdeditor讓管理文本類的資料更加方便
  3. 使用django-debug-toolbar來確認寫ORM時,是否有更高效或是需要做快取的資料
  4. 整合Charts.js到後台,讓呈現資料更輕鬆

今天的範例都沒有做比較深入的應用,主要的目的是要讓大家知道有這些工具可以做應用

再根據個人需求去延伸每一部分的深度與廣度,不知道有沒有藉此激發一些想在後台實施相關功能的想法

明天我們會探討在後台中,Django是怎麼利用User model來進行用戶管理

參考資料

  • django-jazzmin:https://github.com/farridav/django-jazzmin
  • django-mdeditor:https://github.com/pylixm/django-mdeditor/tree/master
  • django-debug-toolbar:https://github.com/jazzband/django-debug-toolbar
  • Chart.js:https://www.chartjs.org/docs/latest/getting-started/

上一篇
Django in 2024: Django Admin二次開發,打造屬於你的後台
下一篇
Django in 2024: 深入瞭解Django內置的用戶管理系統
系列文
Django 2024: 從入門到SaaS實戰31
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言